///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2011 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IO_CSHMHANDLE_H_
#define _BUOLA_IO_CSHMHANDLE_H_

#include <buola/io/cfd.h>
#include <buola/io/cmemmap.h>

namespace buola { namespace io {

/////////////////////////////////////////////////////////////////////////////////////////////////

/// handle to shared-memory segment

/// An object of this class holds a handle (file-descriptor) to a shared-memory segment as
/// provided by the OS. Creating an object of this class doesn't map any memory: It must be
/// explicitly mapped using a CMemMap.
///
/////////////////////////////////////////////////////////////////////////////////////////////////

class CShmHandle : public UUncopiable
{
public:
    /// leaves the handle uninitialized
    
    /// Must be open before it is mapped.
    CShmHandle()
    {}
    
    /// opens a shared-memory segment
    
    /// \param pName the name of the segment (portable format is "/name", the '/' will be automatically appended)
    /// \param pMode one of \c MODE_OPEN, \c MODE_CREATE or \c MODE_OPENORCREATE combined (ORed) with one of 
    /// \c MODE_READ or \c MODE_READWRITE and optionally \c MODE_TRUNCATE.
    /// \param pPerm file permissions to create the object (if it is created)
    CShmHandle(const std::string &pName,EMode pMode,EPermission pPerm=PERM_RW&get_umask())
        :   mFD(nOpenShm,pName,pMode,pPerm,nullptr)
    {}

    /// opens a shared-memory segment

    /// \param pName the name of the segment (portable format is "/name", the '/' will be automatically appended)
    /// \param pMode one of \c MODE_OPEN, \c MODE_CREATE or \c MODE_OPENORCREATE combined (ORed) with one of
    /// \c MODE_READ or \c MODE_READWRITE and optionally \c MODE_TRUNCATE.
    /// \param pCreated is set to true if the file was actually created, to false otherwise. Makes most sense
    /// when \c MODE_OPENORCREATE is specified, but works for all cases
    /// \param pPerm file permissions to create the object (if it is created)
    CShmHandle(const std::string &pName,EMode pMode,bool &pCreated,EPermission pPerm=PERM_RW&get_umask())
        :   mFD(nOpenShm,pName,pMode,pPerm,&pCreated)
    {}
    
    ///copy constructor (deleted)
    CShmHandle(const CSHmHandle &pO)=delete;

    /// move constructor
    CShmHandle(CShmHandle &&pO)
        :   mFD(pO.mFD)
    {
        pO.mFD.Reset();
    }

    /// destructor
    
    /// Destroys the shared-memory object if it is created.
    ~CShmHandle()
    {
        mFD.Close();
    }

    /// map (a part of) this shared memory object
    
    /// Returns a memory mapping object. The mapping will be valid until the memory mapping object is destroyed,
    /// independently of whether the shared memory object is destroyed before
    ///
    /// \param pMode one of \c MODE_READ or \c MODE_READWRITE (compatible with what was used when opening the
    /// object), optionally combined with one or more of \c MODE_PRIVATE, \c MODE_EXEC and \c MODE_FIXEDADDRESS
    /// \param pOffset the offset within the memory object to map from
    /// \param pSize the size of the segment to map. If it is not specified, the whole memory segment is mapped
    /// \param pAddress if specified, a hint on the address where to map the segment. If \c MODE_FIXEDADDRESS is
    /// specified, the segment is exactly mapped at this address.
    CShmMap Map(EMode pMode,off_t pOffset=0,std::size_t pSize=0,void *pHint=nullptr)
    {
        off_t lOffset=round_down_to_multiple(pOffset,CFD::GetPageSize());
        std::ptrdiff_t lExtraOffset=pOffset-lOffset;

        if(!pSize)
        {
            pSize=GetSize();
            if(pOffset>pSize)
                throw XArgument("invalid offset mapping shared memory object");
            pSize-=pOffset;
        }
            
        void *lAddress=mFD.Map(pMode,lOffset,pSize+lExtraOffset,pHint);
        return CShmMap(static_cast<char*>(lAddress)+lExtraOffset,pSize,lExtraOffset);
    }

    /// closes the shared-memory object
    void Close()
    {
        mFD.Close();
    }

    /// sets the size of the memory object
    void Truncate(std::size_t pSize)
    {
        mFD.Truncate(pSize);
    }

    /// returns the size of the memory object
    std::size_t GetSize()
    {
        return mFD.GetSize();
    }

    /// move assignment
    CShmHandle &operator=(CShmHandle &&pRH)
    {
        swap(pRH);
        return *this;
    }

private:
    CFD mFD;
};

/*namespace io*/ } /*namespace buola*/ }

#endif
